#include "GRO_Beacons.h"


namespace GRO
{
    // Static members
    int BeaconObj::connectionID = 0;

    Connection::Connection()
            : observer(NULL)
            , id(BeaconObj::connectionID++)
            , priority(0)
            , stop(false)
    {}
    
    Connection::Connection(const Connection& connection)
            : observer(NULL)
            , id(0)
            , priority(connection.priority)
            , stop(connection.stop)
    {
        //if(connection.beacon != NULL)
            //connection.beacon.addConnection(this);  // Problem: I think 'this' may not be good yet.
    }
        
    Connection::~Connection()
    {
        disconnect();
    }
    
    Connection& Connection::operator=(const Connection& connection)
    {
        if(id != connection.id)
        {
            disconnect();
        }
        
        beacon = connection.beacon;
        observer = connection.observer;
        id = connection.id;
        priority = connection.priority;
        stop = connection.stop;
        
        return *this;
    }


    void Connection::disconnect()
    {
        //if(!beacon.expired())
            //beacon.lock()->disconnect(this);
        if(observer != NULL)
            observer->disconnect(this);
        
        //beacon.reset();
        observer = NULL;
    }

    /*void Connection::disconnect(Beacon* b)
    {
        if(beacon == b)
            beacon = NULL;
    }*/

    void Connection::disconnect(Observer* o)
    {
        if(observer == o)
            observer = NULL;
    }


    void Observer::connect(Connection* c)
    {
        GRO_connections.insert(c);
        c->observer = this;
    }

    void Observer::disconnect(Connection* c)
    {
        GRO_connections.erase(c);
        c->observer = this;
    }
    
    
    
    
    
    
    
    
    
    
        BeaconObj::BeaconObj()
        {}
        BeaconObj::BeaconObj(const BeaconObj& beacon)
        {
            for(set<shared_ptr<Connection>, CompareConnections>::const_iterator e = beacon.connections.begin(); e != beacon.connections.end(); e++)
            {
                if(*e == NULL)
                    continue;
                addConnection((*e));
            }
        }
        BeaconObj::~BeaconObj()
        {
            deleteConnections();
        }
        BeaconObj& BeaconObj::operator=(const BeaconObj& beacon)
        {
            deleteConnections();
            
            for(set<shared_ptr<Connection>, CompareConnections>::iterator e = beacon.connections.begin(); e != beacon.connections.end(); e++)
            {
                if(*e == NULL)
                    continue;
                addConnection((*e));
            }
            
            return *this;
        }
        
        
        void BeaconObj::emit()
        {
            set<shared_ptr<Connection>, CompareConnections> copy = connections;
            // Set off the connections
            for(set<shared_ptr<Connection>, CompareConnections>::iterator e = copy.begin(); e != copy.end(); e++)
            {
                if(*e == NULL)
                    continue;
                (*e)->trigger();
                if ((*e)->stop)
                    break;
            }
        }
        
        // Add a pre-prepared connection
        void BeaconObj::addConnection(Connection* c)
        {
            if(c == NULL)
            {
                return;
            }
            
            if(c->priority == 0)
                c->priority = connections.size()+1;


            //c->beacon = shared_ptr<BeaconObj>(this); // this shared ptr needs to be shared...
            connections.insert(shared_ptr<Connection>(c));
        }
        void BeaconObj::addConnection(const shared_ptr<Connection>& c)
        {
            if(!c)
            {
                return;
            }
            
            if(c->priority == 0)
                c->priority = connections.size()+1;


            //c->beacon = shared_ptr<BeaconObj>(this); // this shared ptr needs to be shared...
            connections.insert(c);
        }
        
        
        shared_ptr<Connection> BeaconObj::getConnection(int connectionID)
        {
            for(set<shared_ptr<Connection>, CompareConnections>::iterator e = connections.begin(); e != connections.end(); e++)
            {
                if((*e)->id == connectionID)
                {
                    return *e;
                }
            }
            return NULL;
        }
        shared_ptr<Connection> BeaconObj::disconnect(int connectionID)
        {
            for(set<shared_ptr<Connection>, CompareConnections>::iterator e = connections.begin(); e != connections.end(); e++)
            {
                if((*e)->id == connectionID)
                {
                    shared_ptr<Connection> c = *e;
                    connections.erase(e);
                    return c;
                }
            }
            return NULL;
        }
        
        void BeaconObj::disconnect(Connection* connection)
        {
            connections.erase(shared_ptr<Connection>(connection));
        }
        
        void BeaconObj::deleteConnections()
        {
            connections.clear();
        }
        
        
        
        
        
        Beacon::Beacon()
            : obj(new BeaconObj)
        {}
        Beacon::Beacon(const Beacon& beacon)
            : obj(beacon.obj)
        {}
        Beacon& Beacon::operator=(const Beacon& beacon)
        {
            obj = beacon.obj;
            return *this;
        }
        
        void Beacon::emit()
        {
            shared_ptr<BeaconObj> b = obj;  // protect it from deleting itself
            obj->emit();
        }
        

}
